package com.filetransfer.filemanager.controller;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.filetransfer.domain.FileInfo;
import com.filetransfer.domain.FileInfoHolder;
import com.filetransfer.domain.FileTransferMessage;
import com.filetransfer.domain.Messages;

public class FileServer extends Thread {


	int[] senderPorts;
	List<ChunkUploaderThread> senderThreads;
	
	String fileDir;
	int managerPort;
	FileInfoHolder holder;
	private boolean runStatus = true;
	private boolean senderThreadError = false;
	private boolean init = false;
	
	private void stopService(){
		runStatus = false;
	}
	
	/**
	 * 
	 * @param amanagerPort
	 * @param asenderPorts
	 * @param afileDir
	 */
	public FileServer(int amanagerPort,int[] asenderPorts,String afileDir){
		this.managerPort = amanagerPort;
		this.senderPorts = asenderPorts;
		this.fileDir = afileDir;
	}
	
	private static Logger logger = Logger.getLogger(FileServer.class);
	
	
	private ServerSocket providerSocket;
	private Socket connection = null;
	private ObjectOutputStream out;
	private ObjectInputStream in;
	
	public void startSenderThreads(){
		try{
			if(senderPorts != null && senderPorts.length > 0){
				senderThreads = new ArrayList<ChunkUploaderThread>();
				for(int p:senderPorts){
					try{
						ChunkUploaderThread thread = new ChunkUploaderThread(p,holder);
						senderThreads.add(thread);
						thread.start();
					}catch(Exception e){
						logger.error("exception while starting this sender thread number:" + p);
					}
				}
			}
			
		}catch(Exception e){
			logger.error("Exception while starting sender threads!");
		}
	}
	
	private boolean init(){
		
		if(!init || providerSocket == null || providerSocket.isClosed() || !providerSocket.isBound()){
			try{
				//1. creating a socket to connect to the server
				providerSocket = new ServerSocket(managerPort, 10);
				init = true;
			}catch(Exception e){
				logger.error("Exception in the init of FileDownloader:" + e.getMessage());
				e.printStackTrace();
				init = false;
			}
		}
		
		
		return init;
	}
	
	/**
	 * 
	 */
	public void run(){

		try{

			while(runStatus && init()){
				//2. Wait for connection
				connection = providerSocket.accept();

				//3. get Input and Output streams
				out = new ObjectOutputStream(connection.getOutputStream());
				out.flush();
				
				in = new ObjectInputStream(connection.getInputStream());
				//wait for the input FileTransferMessage object. process it and send response back after processing the message

				try{
					Object messg = in.readObject();
					if(messg instanceof FileTransferMessage){
						FileTransferMessage outMessg = processMessage((FileTransferMessage)messg);
						out.writeObject(outMessg);
						out.flush();
					}
				}catch(Exception e){
					logger.error("Exception while sending/receving messages in the FileSender:"+e.getMessage());
					e.printStackTrace();
				}
			}
			
		}catch(Exception e){
			logger.error("Exception in the main try/catch of the FileSender:"+e.getMessage());
			e.printStackTrace();
		}finally{
			try{
				closeResources();
				providerSocket.close();
			}catch(Exception e){
				logger.error("Exception while closing resources in FileSender:"+ e.getMessage());
				e.printStackTrace();
			}
		}
		
	}
	
	
	private void closeResources(){
		try{
			if(in != null ){
				in.close();
			}
			if(out != null){
				out.close();
			}
		}catch(Exception e){
			
		}
	}
	
	/**
	 * 
	 * @param input
	 * @return
	 */
	private FileTransferMessage processMessage(FileTransferMessage input){
		FileTransferMessage output = null;
		FileInfo info = null;
		
		if(input != null && !input.isError()){
			
			if(input.getMessageType() == Messages.FILE_LIST_REQUEST){
				/*
				 * Get the file list from fileInfoHolder and put into the message.
				 */
				output = new FileTransferMessage(Messages.FILE_LIST_RESPONSE);
				output.setFileList(holder.getFileInfoList(true));
				
			}else if(input.getMessageType() == Messages.FILE_INFO_REQUEST){
				/*
				 * Check if the file for which info is requested is present 
				 * in the list or not. if not present send error.
				 */
				output = new FileTransferMessage(Messages.FILE_INFO_RESPONSE);
				
				info = input.getFileInfo();
				if(info == null || !holder.getFileInfoList(true).contains(info)){
					output.setError(true);
					output.setErrorMessage("Invalid file requested. File :" + info.getFileName() + " is not present in the list of files available for download. ");
				}else{
					//send the relevant FileInfo
					info = holder.getFileInfoList(false).get(holder.getFileInfoList(false).indexOf(info));
					output.setFileInfo(info);
				}
				
			}else{
				//put a log that the any other messages must not be sent to the server
				logger.warn("Invalid message type sent to the server:" + input.getMessageType() + " with the fileinfo: " + input.getFileInfo());
			}
			
		}
		return output;
	}

}
